package cn.flying.cloud.file.core.ocr.wx;

import javax.annotation.Resource;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;

import cn.flying.cloud.base.common.exception.BizException;
import cn.flying.cloud.base.common.utils.Ft;
import cn.flying.cloud.file.core.ocr.enums.DataTypeEnum;
import cn.flying.cloud.file.core.ocr.enums.OcrTypeEnum;
import cn.flying.cloud.file.core.ocr.model.WxAccessToken;

/**
 * 微信OCR识别
 *
 * @author: admin
 * @date: 2025年07月25日 11:10
 * @version: 1.0
 */
@Component
public class WxOcrService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private WxOcrProperties wxOcrProperties;
    @Resource
    private RestTemplate restTemplate;

    /**
     * 获取接口调用凭据
     *
     * @return
     */
    public WxAccessToken getAccessToken() {
        // 小程序唯一凭证，即 AppID，可在「微信公众平台 - 设置 - 开发设置」页中获得。（需要已经成为开发者，且帐号没有异常状态）
        String appId = wxOcrProperties.getAppId();
        // 小程序唯一凭证密钥，即 AppSecret，获取方式同 appid
        String appSecret = wxOcrProperties.getAppSecret();
        Map<String, Object> param = new HashMap<>();
        param.put("grant_type", "client_credential");
        param.put("appid", appId);
        param.put("secret", appSecret);
        // 默认使用 false。1. force_refresh = false 时为普通调用模式，access_token 有效期内重复调用该接口不会更新 access_token；
        // 2. 当force_refresh = true 时为强制刷新模式，会导致上次获取的 access_token 失效，并返回新的 access_token
        param.put("force_refresh", false);
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param);
        logger.info("获取小程序Token，AppId：{}，AppSecret：{}", appId, appSecret);
        String tokenUrl = "https://api.weixin.qq.com/cgi-bin/stable_token";
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(tokenUrl, requestEntity, String.class);
        logger.info("小程序返回的Token信息：【{}】", Ft.JSON.serializer(responseEntity));
        String body = responseEntity.getBody();
        if (Ft.String.isBlank(body)) {
            throw new BizException("获取小程序Token返回null");
        }
        // access_token	string	获取到的凭证
        // expires_in	number	凭证有效时间，单位：秒。目前是7200秒之内的值
        return Ft.JSON.deserialize(body, WxAccessToken.class);
    }

    /**
     * OCR识别
     *
     * @param imgUrl   图片地址
     * @param imgData  图片数据
     * @param dataType 数据类型
     * @param ocrType  OCR类别
     * @return
     */
    public Map<String, String> ocrAllInOne(String imgUrl, String imgData, DataTypeEnum dataType, OcrTypeEnum ocrType) {
        // 1. 准备请求体数据（可以是普通Java对象）
        Map<String, Object> param = new HashMap<>();
        param.put("service", "wx79ac3de8be320b71");
        param.put("api", "OcrAllInOne");
        Map<String, Object> data = new HashMap<>();
        // img_data	string	是（与img_url二选一）		图片
        // img_url	string	是（与img_data二选一）		图片
        if (DataTypeEnum.URL.getType().equals(dataType.getType())) {
            data.put("img_url", imgUrl);
        } else {
            data.put("img_data", imgData);
        }
        // 1：二进制；2：base64字符串；3：图片url
        data.put("data_type", dataType.getType());
        // 1：身份证；2：银行卡；3：行驶证；4：驾驶证；7：营业执照；8：通用OCR ; 10: 车牌识别
        data.put("ocr_type", ocrType.getType());
        param.put("client_msg_id", UUID.randomUUID().toString());
        logger.info("OCR识别部分请求参数：【{}】", Ft.JSON.serializer(param));
        param.put("data", data);

        // 2. 准备请求头，指定Content-Type为JSON
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        // 3. 封装请求头和请求体
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param, headers);
        // 4. 获取调用凭证
        String accessToken = getAccessToken().getAccessToken();
        String ocrUrl = "https://api.weixin.qq.com/wxa/servicemarket?access_token=" + accessToken;

        // 返回数据对象
        Map<String, String> ocrData = new HashMap<>();

        ResponseEntity<String> responseEntity = restTemplate.postForEntity(ocrUrl, requestEntity, String.class);
        logger.info("OCR识别返回：【{}】", Ft.JSON.serializer(responseEntity));
        String body = responseEntity.getBody();
        if (Ft.String.isBlank(body)) {
            return ocrData;
        }

        // 解析JSON字符串为JSONObject
        JSONObject rootObject = JSON.parseObject(body);
        JSONObject dataObject = rootObject.getJSONObject("data");

        if (OcrTypeEnum.ID.getType().equals(ocrType.getType())) {
            idCard(ocrData, dataObject);
        } else if (OcrTypeEnum.CAR.getType().equals(ocrType.getType())) {
            plateNumber(ocrData, dataObject);
        } else if (OcrTypeEnum.BANK.getType().equals(ocrType.getType())) {
            bankCard(ocrData, dataObject);
        }

        return ocrData;
    }

    /**
     * 提取车牌号
     */
    private void plateNumber(Map<String, String> data, JSONObject root) {
        // {
        //    "image_height": 683,
        //    "image_width":1080,
        //    "debug_info":"{\"text\":\"\\u4eacA33333\",\"score\":0.99999999999999999}",
        //    "number":{
        //    "text":"京A33333"}
        //}

        JSONObject res = root.getJSONObject("plate_comm_res");
        // 从number对象中提取text属性
        JSONObject numberObject = res.getJSONObject("number");
        if (numberObject != null) {
            String plateNumber = numberObject.getString("text");
            data.put("plateNumber", plateNumber);
        }
    }

    /**
     * 提取身份证号
     */
    private void idCard(Map<String, String> data, JSONObject root) {
        //{
        //    "type": 0, //正面
        //    "name": {"text": "张三"}, //姓名
        //    "id": {"text": "123456789012345678"}, //身份证号码
        //    "address": {"text": "广东省广州市XXX"}, //住址
        //    "gender": {"text": "男"}, //性别
        //    "nationality": {"text": "汉"} //民族
        //}

        //{
        //    "type": 1, //背面
        //    "valid_date": {"text": "20171025-20271025"} //有效期
        //}

        JSONObject res = root.getJSONObject("idcard_res");
        // 解析姓名
        JSONObject nameObject = res.getJSONObject("name");
        if (nameObject != null) {
            String name = nameObject.getString("text");
            data.put("name", name);
        }
        // 解析身份证号码
        JSONObject idObject = res.getJSONObject("id");
        if (idObject != null) {
            String id = idObject.getString("text");
            data.put("id", id);
        }
        // 解析住址
        JSONObject addressObject = res.getJSONObject("address");
        if (addressObject != null) {
            String address = addressObject.getString("text");
            data.put("address", address);
        }
        // 解析姓名
        JSONObject genderObject = res.getJSONObject("gender");
        if (genderObject != null) {
            String gender = genderObject.getString("text");
            data.put("gender", gender);
        }
        // 解析民族
        JSONObject nationalityObject = res.getJSONObject("nationality");
        if (nationalityObject != null) {
            String nationality = nationalityObject.getString("text");
            data.put("nationality", nationality);
        }
        // 解析有效期
        JSONObject validDateObject = res.getJSONObject("valid_date");
        if (validDateObject != null) {
            String validDate = validDateObject.getString("text");
            data.put("validDate", validDate);
        }
    }

    /**
     * 提取银行卡
     */
    private void bankCard(Map<String, String> data, JSONObject root) {
        // {
        //    "number": {"text": "1234567890"} //银行卡号
        //}

        JSONObject res = root.getJSONObject("bankcard_res");
        // 银行卡号
        JSONObject numberObject = res.getJSONObject("number");
        if (numberObject != null) {
            String number = numberObject.getString("text");
            data.put("number", number);
        }
    }

}
